package com.alpha.service.annotation;

import com.alpha.core.exception.AlphaException;
import com.alpha.service.context.AlphaApplicationContext;
import com.alpha.service.utils.RedisUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.UUID;

/**
 * 分布式锁注解
 *
 * @author chenruwan
 * @since 2022-01-01
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {
    String value();

    @Aspect
    @Component
    class DistributedLockAspect {
        @Pointcut("@annotation(com.alpha.service.annotation.DistributedLock)")
        public void pointcut() {
            // nothing to do
        }

        @Around("pointcut() && @annotation(distributedLock)")
        public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
            String key = AlphaApplicationContext.getApplicationName() + "::lock::" + distributedLock.value();
            String uuid = UUID.randomUUID().toString();
            if (!RedisUtil.tryLock(key, uuid, RedisUtil.DEFAULT_LOCK_EXPIRE_TIME, RedisUtil.DEFAULT_LOCK_RETRY_TIME)) {
                throw new AlphaException("服务繁忙，请稍后再试。");
            }
            try {
                return joinPoint.proceed();
            } finally {
                RedisUtil.unlock(key, uuid);
            }
        }
    }
}
